package cc.android.testapp.xp.hooker.adblock.track;

import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;

import org.json.JSONObject;

import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;

import cc.android.testapp.util.CLog;
import cc.android.testapp.xp.hooker.adblock.core.AdRule;
import cc.commons.util.StringUtil;
import cc.commons.util.extra.CList;
import cc.commons.util.reflect.MethodUtil;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;

public class ActTrack extends XC_MethodHook {
    public boolean enable = false;
    public WeakReference<Activity> act;

    public Object tran = null;
    public AdRule rule;

    private JSONObject mFragmentJson = new JSONObject();
    private boolean mFragmentType = false;

    public ArrayList<XC_MethodHook.Unhook> hooks = new ArrayList<>();


    public ActTrack(Context pCon, AdRule pRule) {
        this.act = new WeakReference<>((Activity) pCon);
        this.rule = pRule;

        try {
            this.init();
        } catch (Throwable exp) {
            CLog.log("error on init track", exp);
        }
    }

    public void stopTrack() {
        for (XC_MethodHook.Unhook sHook : hooks) {
            sHook.unhook();
        }
        hooks.clear();
        if (mFragmentType) {
            rule.mSType = SkipType.FRAGMENT;
            rule.mFragmentJson = mFragmentJson;
        }
    }

    private void init() {
        hooks.add(XposedHelpers.findAndHookMethod(Activity.class, "finish"
                , new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam pParam) {
                        if (enable && checkTarget(pParam.thisObject)) {
                            CLog.log("finish call_2");
                            if (rule.mSType == SkipType.CLICK_BUTTON) {
                                rule.mSType = SkipType.FINISH_ACT;
                            }
                        }
                    }
                }));

        hooks.add(XposedHelpers.findAndHookMethod(Activity.class, "startActivityForResult", Intent.class, int.class
                , Bundle.class, new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam pParam) {
                        if (enable && checkTarget(pParam.thisObject)) {
                            CLog.log("startActivityForResult call_2");
                            rule.mSType = SkipType.START_ACT;
                            rule.mExtra = ((Intent) pParam.args[0]).toUri(0);
                        }
                    }
                }));

        // Fragment

        Object tMan = TrackHelper.getFragmentManager(act.get());
        Method tMethod = MethodUtil.getMethod(tMan.getClass(), "beginTransaction", false);
        Object finalTMan = tMan;

        hooks.add(XposedBridge.hookMethod(tMethod, new XC_MethodHook() {

            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable { //hook事务开始
                if (act.get() != null && finalTMan == param.thisObject) {
                    tran = param.getResult();
                    XC_MethodHook tHook = new XC_MethodHook() {
                        @Override
                        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                            if (param.thisObject != tran || !enable) return;
                            // 捕获广告Fragment,用于在onCreate和onResume中进行移除
                            CLog.log(param.method.getName() + " fragment: " + Arrays.toString(param.args));
                            Object tFragment = null;
                            String tMethod = param.method.getName();
                            mFragmentJson.put(tMethod, true);
                            if ("add".equals(tMethod) || "replace".equals(tMethod)) {
                                mFragmentJson.put("container_id", param.args[0]);
                                if (param.args[2] != null) {
                                    mFragmentJson.put("fragment_tag", String.valueOf(param.args[2]));
                                }
                                tFragment = param.args[1];
                                mFragmentType = true;
                            } else {
                                //show,remove,hide
                                tFragment = param.args[0];
                            }
                            mFragmentJson.put(tMethod + "_fragment", tFragment.getClass().getName());
                            TrackHelper.fragments.put(tFragment.getClass().getName(), tFragment);
                        }
                    };

                    Method tMethod = MethodUtil.getMethod(tran.getClass(), (method) ->
                            method.getName().equals("add") && method.getParameterTypes().length == 3, true).oneGet();
                    hooks.add(XposedBridge.hookMethod(tMethod, tHook));
                    Arrays.asList("show", "replace", "hide", "remove").forEach((method) -> {
                        Class<?> tClazz = tran.getClass();
                        CList<Method> tMs = null;
                        do {
                            tMs = MethodUtil.getMethodIgnoreParam(tran.getClass(), method, true);
                            if (!tMs.isEmpty()) break;
                            tClazz = tClazz.getSuperclass();
                        } while (tClazz != null);

                        if (tMs == null || tMs.isEmpty()) return;
                        for (Method sMethod : MethodUtil.getMethodIgnoreParam(tran.getClass(), method, true)) {
                            if (!Modifier.isAbstract(sMethod.getModifiers()))
                                hooks.add(XposedBridge.hookMethod(sMethod, tHook));
                        }
                    });
                }
            }
        }));


    }

    private boolean checkTarget(Object pAct) {
        return enable && (act.get() == pAct);
    }


}
